import path from 'path';
import fs from 'fs';
import PouchDB from 'pouchdb';
import { DBError, Doc, DocRes } from './types';

export default class {
  readonly docMaxByteLength;
  readonly docAttachmentMaxByteLength;
  public dbpath;
  public defaultDbName;
  public pouchDB: any;

  constructor(dbPath: string) {
    this.docMaxByteLength = 2 * 1024 * 1024; // 2M
    this.docAttachmentMaxByteLength = 20 * 1024 * 1024; // 20M
    this.dbpath = dbPath;
    this.defaultDbName = path.join(dbPath, 'default');
  }

  init(): void {
    fs.existsSync(this.dbpath) || fs.mkdirSync(this.dbpath);
    this.pouchDB = new PouchDB(this.defaultDbName, { auto_compaction: true });
  }

  async get(name: string, id: string): Promise<DocRes | null> {
    try {
      const result: DocRes = await this.pouchDB.get(this.getDocId(name, id));
      result._id = this.replaceDocId(name, result._id);
      return result;
    } catch (e) {
      return null;
    }
  }

  errorInfo(name: string, message: string): DBError {
    return { error: true, name, message };
  }

  private checkDocSize(doc: Doc<any>) {
    if (Buffer.byteLength(JSON.stringify(doc)) > this.docMaxByteLength) {
      return this.errorInfo(
        'exception',
        `doc max size ${this.docMaxByteLength / 1024 / 1024} M`
      );
    }
    return false;
  }

  async put(
    name: string,
    doc: Doc<any>,
    strict = true
  ): Promise<DBError | DocRes> {
    if (strict) {
      const err = this.checkDocSize(doc);
      if (err) return err;
    }
    doc._id = this.getDocId(name, doc._id);
    try {
      const result: DocRes = await this.pouchDB.put(doc);
      doc._id = result.id = this.replaceDocId(name, result.id);
      return result;
    } catch (e: any) {
      doc._id = this.replaceDocId(name, doc._id);
      return { id: doc._id, name: e.name, error: !0, message: e.message };
    }
  }

  // 获取id
  getDocId(name: string, id: string): string {
    return name + '/' + id;
  }

  /**
   * 替换id
   * @param name
   * @param id
   * @returns
   */
  replaceDocId(name: string, id: string): string {
    return id.replace(name + '/', '');
  }
}
